package org.xg.study.boot.common.error;

import org.xg.study.boot.common.ajax.ResultData;
import org.xg.study.boot.common.logs.CodeBookLogger;
import org.xg.study.boot.common.utils.ResultUtil;
import org.hibernate.validator.internal.engine.path.PathImpl;
import org.springframework.boot.web.server.ConfigurableWebServerFactory;
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.NoHandlerFoundException;

import  org.xg.study.boot.common.constant.ResponseCode;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Path;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author CMG
 * @version 1.0
 * @cLassName GlobalExceptionHandler
 * @description
 * @date 2021/8/25 17:07
 **/
@RestControllerAdvice
public class GlobalExceptionHandler {
    protected CodeBookLogger log = new CodeBookLogger(GlobalExceptionHandler.class);
    /**
     * @return ResultData @Author ldl @Description 请求参数不存在异常拦截 @Date 2021/1/14 @Param
     * MissingServletRequestParameterException
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public ResultData handleError(MissingServletRequestParameterException e) {
        log.warn(10100002,"请求参数不存在", e);
        String message = String.format("请求参数不存在: %s", e.getParameterName());
        return ResultUtil.fail(
                ResponseCode.PARAM_VALID_ERROR.getRetCode(),
                message,
                ResponseCode.PARAM_VALID_ERROR.getRetMsg());
    }

    /**
     * @return ResultData @Author ldl @Description 不合法的请求参数拦截 @Date 2021/1/14 @Param
     * IllegalArgumentException
     */
    @ExceptionHandler(IllegalArgumentException.class)
    public ResultData handleError(IllegalArgumentException e) {
        log.warn(10100003,"不合法的请求参数", e);
        String message = String.format("不合法的请求参数: %s", e.getMessage());
        return ResultUtil.fail(
                ResponseCode.PARAM_VALID_ERROR.getRetCode(),
                message,
                ResponseCode.PARAM_VALID_ERROR.getRetMsg());
    }

    /**
     * @return ResultData @Author ldl @Description 参数未给出为null @Date 2021/1/14 @Param
     * NullPointerException
     */
    @ExceptionHandler(NullPointerException.class)
    public ResultData handleError(NullPointerException e) {
        log.warn(10100004,"参数未给出为null", e);
        String message = String.format("参数未给出,请给出: %s", e.getMessage());
        return ResultUtil.fail(
                ResponseCode.PARAM_MISS.getRetCode(), message, ResponseCode.PARAM_MISS.getRetMsg());
    }

    /**
     * @return ResultData @Author ldl @Description 方法参数类型不匹配异常拦截 @Date 2021/1/14 @Param
     * MethodArgumentTypeMismatchException
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public ResultData handleError(MethodArgumentTypeMismatchException e) {
        log.warn(10100005,"方法参数类型不匹配异常", e);
        String message = String.format("Method Argument Type Mismatch: %s", e.getName());
        return ResultUtil.fail(
                ResponseCode.PARAM_VALID_ERROR.getRetCode(),
                message,
                ResponseCode.PARAM_VALID_ERROR.getRetMsg());
    }

    /**
     * @return ResultData @Author ldl @Description 请求体中参数无效异常拦截 @Date 2021/1/14 @Param
     * MethodArgumentNotValidException
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public ResultData<List<ValidParamMsg>> handleBodyValidParam(MethodArgumentNotValidException e) {
        log.warn(10100006,"方法参数无效异常", e);
        // 获取所有捕获到的异常信息，并映射成为无效参数目标实体列表
        List<ObjectError> allErrors = e.getBindingResult().getAllErrors();
        List<ValidParamMsg> validParamList = allErrors.stream().map(objectError -> {
            FieldError fieldError = (FieldError) objectError;
            return new ValidParamMsg(fieldError.getField(), fieldError.getDefaultMessage());
        }).collect(Collectors.toList());

        return ResultUtil.fail(validParamList, ResponseCode.PARAM_VALID_ERROR.getRetCode(), "参数不合法: " + validParamList.get(0).getErrorMsg(), "参数不合法，请检查输入参数");
    }

    /**
     * @return ResultData @Author ldl @Description 绑定异常拦截 @Date 2021/1/14 @Param BindException
     */
    @ExceptionHandler(BindException.class)
    public ResultData handleError(BindException e) {
        log.warn(10100007,"绑定异常", e);
        FieldError error = e.getFieldError();
        String message = String.format("%s:%s", error.getField(), error.getDefaultMessage());
        return ResultUtil.fail(
                ResponseCode.PARAM_BIND_ERROR.getRetCode(), ResponseCode.PARAM_BIND_ERROR.getRetMsg(), message);
    }

    /**
     * @return ResultData @Author ldl @Description 方法参数列表中数据效验异常拦截 @Date 2021/1/14 @Param
     * ConstraintViolationException
     */
    @ExceptionHandler(value = ConstraintViolationException.class)
    @ResponseBody
    public ResultData<List<ValidParamMsg>> exception(ConstraintViolationException e) {
        log.warn(10100008,"方法参数无效异常", e);
        // 获取所有捕获到的异常信息，并映射成为无效参数目标实体列表
        Set<ConstraintViolation<?>> constraintViolationSet = e.getConstraintViolations();
        List<ValidParamMsg> validParamList = constraintViolationSet.stream().map(constraintViolation -> {
            Path propertyPath = constraintViolation.getPropertyPath();
            String field = ((PathImpl) propertyPath).getLeafNode().getName();
            String errorMsg = constraintViolation.getMessage();
            return new ValidParamMsg(field, errorMsg);
        }).collect(Collectors.toList());

        return ResultUtil.fail(validParamList, ResponseCode.PARAM_VALID_ERROR.getRetCode(), "参数不合法", "参数不合法，请检查输入参数");
    }

    /**
     * @return ResultData @Author ldl @Description 404异常拦截 @Date 2021/1/14 @Param
     * NoHandlerFoundException
     */
    @ExceptionHandler(NoHandlerFoundException.class)
    public ResultData handleError(NoHandlerFoundException e) {
        log.error(e,10100009,"404未找到异常", e);
        return ResultUtil.fail(
                ResponseCode.NOT_FOUND.getRetCode(), e.getMessage(), ResponseCode.NOT_FOUND.getRetMsg());
    }

    /**
     * @return ResultData @Author ldl @Description 消息不可读异常拦截 @Date 2021/1/14 @Param
     * HttpMessageNotReadableException
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResultData handleError(HttpMessageNotReadableException e) {
        log.error(e,10100010,"消息不可读异常", e);
        return ResultUtil.fail(
                ResponseCode.NOT_FOUND.getRetCode(), e.getMessage(), ResponseCode.NOT_FOUND.getRetMsg());
    }

    /**
     * @return ResultData @Author ldl @Description 请求方法不支持异常拦截 @Date 2021/1/14 @Param
     * HttpRequestMethodNotSupportedException
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ResultData handleError(HttpRequestMethodNotSupportedException e) {
        log.error(e,10100011,"请求方法不支持异常", e);
        return ResultUtil.fail(
                ResponseCode.NOT_FOUND.getRetCode(), e.getMessage(), ResponseCode.NOT_FOUND.getRetMsg());
    }

    /**
     * @return ResultData @Author ldl @Description 媒体类型不支持异常拦截 @Date 2021/1/14 @Param
     * HttpMediaTypeNotSupportedException
     */
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public ResultData handleError(HttpMediaTypeNotSupportedException e) {
        log.error(e,10100012,"媒体类型不支持异常", e);
        return ResultUtil.fail(
                ResponseCode.NOT_FOUND.getRetCode(), e.getMessage(), ResponseCode.NOT_FOUND.getRetMsg());
    }

    /**
     * @return ResultData @Author ldl @Description 服务器内部错误拦截 @Date 2021/1/14 @Param ServiceException
     */
    @ExceptionHandler(ServiceException.class)
    public ResultData handleError(ServiceException e) {
        log.error(e,10100013, "服务器内部错误异常", e);
        return ResultUtil.fail(
                ResponseCode.INTERNAL_SERVER_ERROR.getRetCode(),
                e.getMessage(),
                ResponseCode.INTERNAL_SERVER_ERROR.getRetMsg());
    }

    /**
     * @param e Throwable
     * @return ResultData @Author ldl @Description 服务器内部错误拦截 @Date 2021/1/14
     */
    @ExceptionHandler(Throwable.class)
    public ResultData handleError(Throwable e) {
        log.error(e,10100014,"服务器内部错误异常", e);
        return ResultUtil.fail(
                ResponseCode.INTERNAL_SERVER_ERROR.getRetCode(),
                e.getMessage(),
                ResponseCode.INTERNAL_SERVER_ERROR.getRetMsg());
    }

    /**
     * 404处理
     *
     * @return
     */
    @Bean
    public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryCustomizer() {
        return (factory -> {
            ErrorPage error404Page = new ErrorPage(HttpStatus.NOT_FOUND, "/404");
            factory.addErrorPages(error404Page);
        });
    }
}
